﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core;
using COFE.Core.Utils;
using COFE.Core.Profile;
using System.IO;
using NUnit.Framework;

namespace COFE.Core.Utils
{
    /// <summary>
    /// COFE version of System.IO.File static class, provide methods to manipulate files.
    /// </summary>
    public class FileServices : IFileServices
    {
        #region Constructors

        internal FileServices(IUserProfile profile)
        {
            UserProfile = profile;
        }

        #endregion

        #region Methods


        public void Move(string srcFilePath, string destFilePath)
        {
            IFileInfo file = UserProfile.ParseNonCachePath(srcFilePath) as IFileInfo;
            Copy(srcFilePath, destFilePath);
            file.Delete();
        }

        public void Copy(string srcFilePath, string destFilePath)
        {
            IFileInfo file = UserProfile.ParseNonCachePath(srcFilePath) as IFileInfo;
            string destPath = COFEPath.GetDirectoryName(destFilePath);
            string destName = COFEPath.GetFileName(destFilePath);

            UserProfile.Directory.CreateDirectory(destPath);
            IDirectoryInfo destDir = UserProfile.ParseNonCachePath(destPath) as IDirectoryInfo;

            Assert.IsNotNull(destDir);

            using (var inputStream = file.OpenRead())
            using (var outputStream = OpenWrite(destFilePath))
                StreamUtils.CopyStream(inputStream, outputStream, false, false, true);

            Assert.IsNotNull(UserProfile.TryParsePath(destFilePath));


            //IFileInfo sourceInfo = UserProfile.ParsePath(srcFilePath) as IFileInfo;
            //string destDir = COFEPath.GetDirectoryName(destFilePath); //Parent directory
            //if (COFEPath.GetFileName(srcFilePath) != COFEPath.GetFileName(destFilePath))
            //    throw new NotImplementedException("Rename while copy is not supported yet.");
            //IDirectoryInfo destInfo = (IDirectoryInfo)UserProfile.TryParsePath(destDir);
            //if (sourceInfo == null) throw new System.IO.FileNotFoundException("Source not found", srcFilePath);
            //if (destInfo == null) throw new System.IO.FileNotFoundException("Destation directory not found", destDir);
            //EntryUtils.Copy(sourceInfo, destInfo);
        }

        public bool Exists(string path)
        {
            bool isFolder;
            return COFEShared.PathParser.Exists(COFEShared.CurrentUserProfile, path, ParsableType.ParsePath, out isFolder) && !isFolder;
            //IFileInfo file = UserProfile.TryParsePath(path) as IFileInfo;
            //return file != null;
        }

        public void Delete(string path)
        {
            IFileInfo file = UserProfile.TryParsePath(path, 
                EntryConstructionFlags.DefaultWithoutCacheWithLookup) as IFileInfo;
            if (file != null)
                file.Delete();
        }

        public System.IO.Stream Open(string path, System.IO.FileMode mode, 
            System.IO.FileAccess access)
        {
            Func<IFileSystemInfo, IFileInfo> getFile = (e) =>
            {
                if (e is IFileInfo)
                    return e as IFileInfo;
                else if ((e as IInternalDirectoryInfoExA).IsFileBasedFS)
                    return ((e as IInternalDirectoryInfoExA).FileBasedFSFile);
                else throw new ArgumentException(e.ParseName + " is not a file.");

            };

            bool exists = Exists(path);

            if (exists)
                try
                {
                    return getFile(UserProfile.ParseNonCachePath(path)).Open(mode, access);
                }
                catch (System.IO.FileNotFoundException)
                {
                    exists = false;
                }

            //Not exists
            string parentPath = COFEPath.GetDirectoryName(path);
            string fileName = COFEPath.GetFileName(path);

            UserProfile.Directory.CreateDirectory(parentPath);
            IInternalDirectoryInfoExA dir = UserProfile.ParseNonCachePath(parentPath) as IInternalDirectoryInfoExA;
            Assert.IsNotNull(dir);

            var newFile = dir.DirectoryController.CreateFile(dir, fileName);
            if (mode == FileMode.CreateNew)
                return getFile(newFile).Open(FileMode.Create, access);
            else return getFile(newFile).Open(mode, access);


        }

        public System.IO.Stream Open(string path, System.IO.FileMode mode)
        {
            FileAccess fa = FileAccess.Read;
            switch (mode)
            {
                case FileMode.Append:
                    fa = FileAccess.ReadWrite;
                    break;
                case FileMode.Create:
                case FileMode.CreateNew:
                case FileMode.OpenOrCreate:
                case FileMode.Truncate:
                    fa = FileAccess.Write;
                    break;
            }
            return Open(path, mode, fa);
        }

        public System.IO.Stream Open(string path)
        {
            return Open(path, FileMode.Open);
        }

        public System.IO.Stream OpenWrite(string path)
        {
            return Open(path, FileMode.OpenOrCreate);
        }


        public System.IO.Stream OpenRead(string path)
        {
            return Open(path, FileMode.Open);
        }

        public System.IO.StreamWriter AppendText(string path)
        {
            return new StreamWriter(Open(path, FileMode.Append));
        }

        public System.IO.StreamReader OpenText(string path)
        {
            return new StreamReader(Open(path));
        }

        public System.IO.Stream Create(string path)
        {
            return Open(path, FileMode.CreateNew);
        }

        #endregion

        #region Data

        #endregion


        #region Public Properties

        public IUserProfile UserProfile { get; set; }

        #endregion

    }
}
